Android 应用启动性能 | 延迟初始化
应用启动 (App Startup)
https://developer.android.google.cn/topic/libraries/app-startup
使用应用启动库自动初始化
// 查看最新的版本号 https://developer.android.google.cn/jetpack/androidx/releases/startup
def startup_version = "1.0.0"
implementation “androidx.startup:startup-runtime:$startup_version”
class MyWorkManagerInitializer : Initializer<WorkManager> {
override fun create(context: Context): WorkManager {
val configuration = Configuration.Builder().build()
WorkManager.initialize(context, configuration)
return WorkManager.getInstance(context)
}
override fun dependencies(): List<Class<out Initializer<*>>> {
// 没有其他依赖库
return emptyList()
}
}
应用启动使用手册
https://developer.android.google.cn/topic/libraries/app-startupWorkManager’s content provider
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:work/workmanager/src/main/java/androidx/work/WorkManagerInitializer.java;l=30?q=WorkManagerIniti&ss=androidx
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:exported="false"
tools:node="remove" />
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data android:name="com.example.startuplibtest.MyWorkManagerInitializer"
android:value="androidx.startup" />
</provider>
tools:node="merge"
这个参数主要用于 Android Studio 所负责的 manifest 合并操作。它告诉工具在最终合并的 manifest 文件中合并这个标签的多个实例。在这个例子中,它会合并由库依赖自动生成的 <provider> 到这个版本的 provider,这样在最终合并的 manifest 文件中只会有这一个标签实例。
另一行包含了这个 meta-data:
<meta-data android:name="com.example.startuplibtest.MyWorkManagerInitializer"
android:value="androidx.startup" />
偷个懒...如果您想的话
使用应用启动库实现延迟初始化
现在我们已经知道该如何使用应用启动库实现自动加载以及初始化库。接下来让我们更进一步地来看看,如果您不想在启动的时候初始化,该如何实现延迟初始化。
其实上面的代码已经很接近了,在 build.gradle 文件中您需要同样的启动依赖和其他您想使用的库,也还是需要特殊的 "移除" provider 标签来去除每个库自动生成的 content provider。我们只需要向 manifest 文件添加多一点信息来告诉它同样移除应用启动库的 provider。这样在应用启动的时候就不会有任何 content provider 初始化发生,而完全由您来决定什么时候应该触发相关初始化。
为了达到这个目的,我用下面的代码替换了前面使用的 InitializationProvider。上面所展示的代码告诉了系统该如何定位 content provider 中自动初始化您库的代码。因为稍后要手动触发初始化,这一次我要跳过那个部分,而只留下在应用启动的时候去除自动生成的 content provider 的部分。
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
在我做了这个改动后,在合并的 manifest 文件中不再有任何 content provider 了,所以应用启动库和 WorkManager 都不会在应用启动的时候被自动初始化了。
为了手动初始化这些库,我在应用的其他地方添加了如下代码来实现它:
val initializer = AppInitializer.getInstance(context)
initializer.initializeComponent(MyWorkManagerInitializer::class.java)
AppInitializer 是应用启动库提供的连接所有这些部分的类。您需要使用一个 context 对象来创建 AppInitializer 对象,然后可以向其传递一个您为初始化各种不同库创建的 Initializer 引用。在这个示例中,我使用的是 MyWorkManagerInitializer,然后就搞定了。
时间就是一切
我做了几次测试 (使用的是我在测试应用启动性能文章中提到的计时方法) 来比较几种不同的启动应用和初始化库的方法。我统计了不带任何库、带 WorkManager (使用默认自动生成的 content provider)、在启动时使用应用启动库自动初始化 WorkManager 以及使用 AppInitializer 延迟初始化 WorkManager 和应用启动库。
需要注意的是,就像我们在之前的文章中讨论的,所有的这些时间计算都是基于锁定的 CPU 主频,所以这些时长都要比在没有锁定 CPU 主频的机器上大很多。它们只在相互之间比较的时候有意义,而并不能代表真实的情况。下面是我发现的:
不带 WorkManager: 1244 ms 带 WorkManager 并且通过 content provider 加载: 1311 ms
带 WorkManager 并且通过 App Startup 加载: 1315 ms
带 WorkManager (延迟加载): 1268 ms
最后,我统计了利用 AppInitalizer 手动初始化 WorkManager 的耗时:
利用 AppInitializer 初始化 WorkManager: 51 ms
检查您项目合并后的 manifest 文件。您可以看到多少 <provider> 标签?
您能否利用应用启动库从合并的 manifest 文件中移除一些甚至所有这些 content provider,并观察它如何影响启动时间?您能否在实现这个的同时不影响运行时行为呢?(值得注意的是: 您需要保证在应用开始依赖相关库的功能之前,确保初始化它们。)
最后,尽情享受性能测试和优化。我会继续找寻更多分析和优化应用的性能办法,如果发现什么有价值的东西我会发布相关的内容。
推荐阅读